<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>
<script>
     /* var ob = {
        // 消息盒子 {click:[fn1,fn2],aaa:[fn1,fn2]}
        message:{
            // "abc":[callback],
            // "abc":[callback,callback]
        }, 
        // 添加订阅 => 在消息盒子中,对应事件类型,存储函数(可以执行的函数不止一个)
        on:function(type,callback){  // ob.on
            if(!this.message[type]){
                this.message[type] = [ callback ]
            }else{
                this.message[type].push(callback);
            }

        }, 
        // 发布事件 => 对应事件类型执行数组中的所有函数
        emit:function(type){

        }, 
        // 取消订阅   消息盒子中,对应事件类型,清除函数(对应函数引用清除)
        off:function(type,callback){
        }, 
     } */

</script>
<script>
    // 观察者模式
    // 观察者模式，通常也被叫做 发布-订阅模式 或者 消息模式, 英文名称叫做 Observer
    // 官方解释： 当一个对象的状态发生改变时，所有依赖于它的对象都得到通知并被自动更新，解决了主体对象与观察者之间功能的耦合，即一个对象状态改变给其他对象通知的问题
    // 听起来很迷糊，但是其实没有很难

    // #### 书写代码

    // 首先我们分析功能

    // - 我们要有一个观察者（这里抽象为一个对象 {}）
    // - 需要有一个属性，存放消息的盒子（把你绑定的所有事件放在里面-> 对应类型绑定函数）
    // - 需要一个 on 方法，用于添加事件
    // - 需要一个 emit 方法，用于发布事件（触发）
    // - 需要一个 off 方法，把已经添加的方法取消

    // 字面量对象  => 复用比较麻烦
    // var observer = {
    //     message:{},  // 消息盒子 {click:[fn1,fn2],aaa:[fn1,fn2]}
    //     on:function(type,callback){},  // 添加订阅 => 在消息盒子中,对应事件类型,存储函数(可以执行的函数不止一个)
    //     emit:function(type){},  // 发布事件 => 对应事件类型执行数组中的所有函数
    //     off:function(type,callback){},  // 取消订阅   消息盒子中,对应事件类型,清除函数(对应函数引用清除)
    //  }


    function Observer(){
        this.message = {
            // "aaa":[callback],  没有就新增 => 新增到数组中
            // "bbb":[callback1,callback2]  有就追加
        };
    }


    // 添加订阅 => 在消息盒子中,对应事件类型(自定义事件),存储函数(可以执行的函数不止一个)
    // type  自定义事件的类型
    // callback  自定义事件触发时执行的回调函数 (函数可以有一个参数 用于接收事件触发时传入的实际参数)
    Observer.prototype.on = function(type,callback){
        // this -> 调用此方法的实例化对象 ({ message })
        // 对应事件类型 将函数存储到消息盒子中 
        // 判断message中是否存在对应类型  this.message.hasOwnProperty(type)
        if(!this.message[type]){ // 对应类型在消息盒子中不存在
            // 新增该类型到消息盒子中
            this.message[type] = [callback];
        }else{   // 对应类型在消息盒子存在
            this.message[type].push(callback);
        }
        return this;
    }

    Observer.prototype.emit = function(type,arg){
        // this -> 调用此方法的实例化对象 ({ message })

        // 在消息盒子中触发指定类型的事件 (前提  有 => 才能触发 没有=> 就跳过)
        
        //  对应类型在消息盒子中不存在
        if(!this.message[type]) return;

        // 模拟事件对象
        // var evt = {
        //     type:type,
        //     arg:arg,
        //     target:this,
        // }
        
        // 存在
        this.message[type].forEach(callback => callback.call(this,arg));

    }

    Observer.prototype.off = function(type,callback){
        // this -> 调用此方法的实例化对象 ({ message })

        // 在消息盒子中对应类型清除数组中的函数 (前提  有类型 => 才能触发 没有对应类型=> 就跳过)

        //  对应类型在消息盒子中不存在  => 
        // if(!this.message[type]) return ; 
        // // 单个清除 => 对应回调函数(两个参数) => 麻烦(先找函数下标在清除)
        // var index = this.message[type].findIndex(fn => fn === callback);  //从消息盒子中对应类型找到函数
        // if(index != -1){  // 有函数在清除
        //     this.message[type].splice(index,1);
        // }

        if(arguments.length >= 1){  // 至少一个参数  ob.off("abc",handerABC_1);   ob.off("abc");

            //  对应类型在消息盒子中不存在  => 
            // if(!this.message[type]) return ; 

            if(arguments.length>=2){  // ob.off("abc",handerABC_1);
                var index = this.message[type].findIndex(fn => fn === callback);  //从消息盒子中对应类型找到函数
                if(index != -1){  // 有函数在清除
                    this.message[type].splice(index,1);
                }
            }else{  // 对应类型清除  ob.off("abc");
                delete this.message[type];
            }
        }
        else{  // 清除所有类型的事件    ob.off();
            this.message = {};
        }

    }



    var ob = new Observer();
    console.log(ob);


    var handerABC_1 = function(arg){
        console.log("this",this);
        console.log("自定义事件abc触发了1111",arg);
    }
    var handerABC_2 = function(arg){
        console.log("自定义事件abc触发了2222",arg);
    }

    var handerQWE_1 = function(arg){
        console.log("自定义事件qwe触发了1111",arg);
    }
    var handerQWE_2 = function(arg){
        console.log("自定义事件qwe触发了2222",arg);
    }

    // 订阅
    ob.on("abc",handerABC_1);
    ob.on("abc",handerABC_2);

    ob.on("qwe",handerQWE_1);
    ob.on("qwe",handerQWE_2);
    
    
    // debugger;
    // ob.emit("abc",1);
    
    
    // ob.off("abc",handerABC_1);
    // ob.off("abc",handerABC_2);
    // ob.off("abc");
    // debugger;
    // ob.off();

    
    
    ob.emit("abc",1);
    ob.emit("qwe",2);
</script>
</html>